home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AOL File Library: 2,801 to 2,900
/
aol-file-protocol-4400-2801-to-2900.zip
/
AOLDLs
/
C++ Files Library
/
SK (Sockects) 1.4.1 r2
/
SK v1.4.1 r2.sit
/
SK 1.4.1 r2
/
SK
/
SK Sources
/
SK_INET.cc
< prev
next >
Wrap
Text File
|
1994-06-13
|
22KB
|
972 lines
/***************************************************************************
*
*
* Copyright ⌐ 1992-1994 Matthias Neeracher and the Decision Systems Group
*
* Permission is granted to anyone to use this software for any purpose on
* any computer system, and to redistribute it freely, subject to the
* following restrictions:
*
* 1) The authors and the Decision Systems Group are not responsible for
* the direct or indirect consequences of use of this software, no matter
* how awful, even if they arise from defects in the software itself.
* This restriction applies to the use of this and any derived source code
* and also to the use of all binary produced from this and any derived
* source code.
* 2) The origin and copyrights of this software must not be misrepresented,
* either by explicit claim or by omission or alteration of copyright or
* authorship header information in this file or in any derived file.
* 3) Altered or derived versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* We encourage users of this software to provide feedback, bug fixes,
* and enhancements to the authors for incorporation into future releases.
*
* ==========================================================================
*
* FILE: SK_INET.cc
*
* AUTHOR: Matthias Neeracher and Stephan Deibel
*
* CREATION DATE: 12Aug92
*
* VERSION: 13Jun94
*
* DESCRIPTION:
*
* General routines for TCP and UDP sockets
*
* NOTES:
*
* 0) This code was derived from Matthias Neerarcher's GUSI 1.1.0 and
* the socket library written by Charlie Reiman (creiman@ncsa.uiuc.edu)
* and Tom Milligan (milligan@madhaus.utcs.utoronto.ca); Copyrights
* are subject to this origin.
*
* MODIFICATIONS:
* --------------------------------------------------------------------------
* Date Name Description of modification
* --------------------------------------------------------------------------
*
* 16Aug92 MN Split
* 20Aug92 MN Wrote most of the functions
* 23Aug92 MN CSK_INETSocket::Available()
* 31Jan93 MN Inetd support
* 07Feb93 MN New configuration technique
* 13Jul93 SD Port to THINK C++ 6.0
* 21Jul93 SD Subsetted to only TCP/UDP, applied DSG naming
* conventions and commenting guidelines, and
* changed name to "SK" for clear distinction from
* the GUSI code from which this was derived
* 13Jun94 SD Updated based on relevant changes to GUSI (through 1.4.1)
*
*/
#include "SK_INET_P.hh"
#include <machine/endian.h>
#include <AppleEvents.h>
/**************************************************************************/
/**************************************************************************/
/* Constants */
#define SKk_Num_PBs 32
/**************************************************************************/
/**************************************************************************/
/* Globals */
CSK_INETSocketDomain SKg_INETSockets;
/**************************************************************************/
/**************************************************************************/
/* Utilities */
/*************************************************************************** *
* Miscellaneous type conversion functions */
unsigned long (htonl)(unsigned long h)
{
return h;
}
unsigned short (htons)(unsigned short h)
{
return h;
}
unsigned long (ntohl)(unsigned long n)
{
return n;
}
unsigned short (ntohs)(unsigned short n)
{
return n;
}
/*************************************************************************** *
* FUNCTION:
*
* SK_TCPError
*
* DESCRIPTION:
*
* Our resident MacTCP error expert. Convert MacTCP err codes into Unix
* error codes.
*
* PARAMETERS:
*
* int -- Mac TCP error code
*
* RETURNS:
*
* int -- Unix error code
*
*/
int SK_TCPError(int MacTCPerr)
{
switch ( MacTCPerr ) {
case 0:
return 0;
case ipBadLapErr:
case ipBadCnfgErr:
case ipNoCnfgErr:
case ipLoadErr:
case ipBadAddr:
errno = ENXIO; /* device not configured */ /* a cheap cop out */
break;
case connectionClosing:
errno = ESHUTDOWN; /* Can't send after socket shutdown */
break;
case connectionExists:
errno = EISCONN; /* Socket is already connected */
break;
case connectionTerminated:
errno = ENOTCONN; /* Connection reset by peer */ /* one of many possible */
break;
case openFailed:
errno = ECONNREFUSED; /* Connection refused */
break;
case duplicateSocket: /* technically, duplicate port */
errno = EADDRINUSE; /* Address already in use */
break;
case ipDestDeadErr:
errno = EHOSTDOWN; /* Host is down */
break;
case ipRouteErr:
errno = EHOSTUNREACH; /* No route to host */
break;
default:
errno = MacTCPerr > 0 ? MacTCPerr : EFAULT; /* cop out; an internal err, unix err, or no err */
break;
}
return -1;
}
/*************************************************************************** *
* FUNCTION:
*
* SK_TCPPossession
*
* DESCRIPTION:
*
* INETD support / for TCP
*
* PARAMETERS:
*
* AppleEvent * -- incoming message
* AppleEvent * -- unused reply
* long -- unused refin
*
* RETURNS:
*
* OSErr -- Error return (0 = no error)
*
* NOTES:
*
* This is a pascal callable function because it is installed as a callback
* used with the Macintosh toolbox Apple Event Manager.
*
*/
pascal OSErr
SK_TCPPossession(AppleEvent* messagein, AppleEvent*, long)
{
AEDesc streamDesc;
OSErr theErr;
CSK_TCPSocket *sock;
if ((theErr = AEGetParamDesc(messagein, 'STRM', typeLongInteger, &streamDesc)) == noErr) {
HLock(streamDesc.dataHandle);
sock = new CSK_TCPSocket(*((StreamPtr*) *(streamDesc.dataHandle)));
HUnlock(streamDesc.dataHandle);
SKg_SocketTable.Possess(0, sock);
SKg_SocketTable.Possess(1, sock);
SKg_SocketTable.Possess(2, sock);
theErr = AEDisposeDesc(&streamDesc);
}
return theErr;
}
/*************************************************************************** *
* FUNCTION:
*
* SK_TCPPossession
*
* DESCRIPTION:
*
* INETD support / for UDP
*
* PARAMETERS:
*
* AppleEvent * -- incoming message
* AppleEvent * -- unused reply
* long -- unused ref_in
*
* RETURNS:
*
* OSErr -- Error return (0 = no error)
*
* NOTES:
*
* This is a pascal callable function because it is installed as a callback
* used with the Macintosh toolbox Apple Event Manager.
*
*/
pascal OSErr
SK_UDPPossession(AppleEvent* messagein, AppleEvent*, long)
{
AEDesc streamDesc;
OSErr theErr;
CSK_UDPSocket *sock;
if ((theErr = AEGetParamDesc(messagein, 'STRM', typeLongInteger, &streamDesc)) == noErr) {
HLock(streamDesc.dataHandle);
sock = new CSK_UDPSocket(*((StreamPtr*) *(streamDesc.dataHandle)));
HUnlock(streamDesc.dataHandle);
SKg_SocketTable.Possess(0, sock);
SKg_SocketTable.Possess(1, sock);
SKg_SocketTable.Possess(2, sock);
theErr = AEDisposeDesc(&streamDesc);
}
return theErr;
}
/**************************************************************************/
/**************************************************************************/
/* CSK_INETSocket members */
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::CSK_INETSocket
*
* DESCRIPTION:
*
* Constructor for this class
*
*/
CSK_INETSocket::CSK_INETSocket()
: CSK_Socket()
{
bzero(&fMySocketAddress, sizeof(struct sockaddr_in));
bzero(&fPeerSocketAddress, sizeof(struct sockaddr_in));
fMySocketAddress.sin_family = AF_INET;
fMySocketAddress.sin_len = sizeof(struct sockaddr_in);
fStatus = SKk_STATUS_USED;
fNonBlocking = false;
SKg_INETSockets.OpenSocket();
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::CSK_INETSocket
*
* DESCRIPTION:
*
* Constructor for this class
*
* PARAMETERS:
*
* StreamPtr -- Stream to use for this internet socket
*
*/
CSK_INETSocket::CSK_INETSocket(StreamPtr stream)
: CSK_Socket(), fStream(stream)
{
fMySocketAddress.sin_family = AF_INET;
fMySocketAddress.sin_len = sizeof(struct sockaddr_in);
fPeerSocketAddress.sin_family= AF_INET;
fPeerSocketAddress.sin_len = sizeof(struct sockaddr_in);
fStatus = SKk_STATUS_USED;
fSocketState = SKk_STATE_CONNECTED;
fNonBlocking = false;
SKg_INETSockets.OpenSocket();
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::~CSK_INETSocket
*
* DESCRIPTION:
*
* Destructor for this class
*
*/
CSK_INETSocket::~CSK_INETSocket()
{
SKg_INETSockets.CloseSocket();
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::Available
*
* DESCRIPTION:
*
* Compute the number of bytes available for reading on this socket.
*
* RETURNS:
*
* Number of bytes available, or 0 if none.
*
* NOTES:
*
* This member function must be overridden and implemented by descendents.
*
*/
unsigned long CSK_INETSocket::Available()
{
return 0;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::bind
*
* DESCRIPTION:
*
* Requests that the name (ip address and port) pointed to by
* name be assigned to this socket.
*
* PARAMETERS:
*
* void * -- The ip address to bind to
* int -- The length of the given ip address
*
* RETURNS:
*
* The return value is 0 on success or -1 if an error occurs,
* in which case global variable errno is set to one of:
*
* EAFNOSUPPORT The address family in name is not AF_INET.
*
* EINVAL The socket is already bound to an address.
*
* EADDRNOTAVAIL The specified address is not available
* from the local machine. ie. the address
* portion of name was not this machine's address.
*
* NOTES:
*
* MacTCP does not separate name binding and connection establishment.
* Therefore the port number is not verified, just stored for later use.
*
* If a specific local port is not required, bind is optional in this
* implementation.
*
*/
int CSK_INETSocket::bind(void * addr, int namelen)
{
struct sockaddr_in *name = (struct sockaddr_in *) addr;
if (namelen < sizeof(struct sockaddr_in))
return SK_Error(EINVAL);
if (name->sin_family != AF_INET)
return SK_Error(EAFNOSUPPORT);
/* already bound */
if (fMySocketAddress.sin_port != 0)
return SK_Error(EINVAL);
/* If client passed a local IP address, assure it is the right one */
if (name->sin_addr.s_addr != 0)
{
struct GetAddrParamBlock pbr;
pbr.ioCRefNum = SKg_INETSockets.Driver();
pbr.csCode = ipctlGetAddr;
if (PBControlSync(ParmBlkPtr(&pbr)))
return SK_Error(ENETDOWN);
if (name->sin_addr.s_addr != pbr.ourAddress)
return SK_Error(EADDRNOTAVAIL);
}
/* NOTE: can't check a TCP port for EADDRINUSE just save the address
and port away for connect or listen or... */
fMySocketAddress.sin_addr.s_addr = name->sin_addr.s_addr;
fMySocketAddress.sin_port = name->sin_port;
return 0;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::getsockname
*
* DESCRIPTION:
*
* Get the current name for this socket.
*
* PARAMETERS:
*
* void * -- Pointer to memory for return value
* int * -- Pointer to memory for return value. Should be initialized
* the amount of space provided for name return
*
* RETURNS:
*
* The function return value indicates error status (0 = no error). When it is
* -1, a more specific return value is in the global errno.
*
* The name is returned in the given memory, truncated to available space
* if necessary. The length of the name (or truncated name) is returned
* in the given int * parameter.
*
*/
int CSK_INETSocket::getsockname(void *name, int *namelen)
{
if (*namelen < 0)
return SK_Error(EINVAL);
memcpy(name, &fMySocketAddress, *namelen = MIN(*namelen, sizeof(struct sockaddr_in)));
return 0;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::getpeername
*
* DESCRIPTION:
*
* Returns the name of the (remote) peer connected to this socket
*
* PARAMETERS:
*
* void * -- Pointer to memory for return value
* int * -- Pointer to memory for return value. Should be initialized
* the amount of space provided for name return
*
* RETURNS:
*
* The function return value indicates error status (0 = no error). When it is
* -1, a more specific return value is in the global errno.
*
* The name is returned in the given memory, truncated to available space
* if necessary. The length of the name (or truncated name) is returned
* in the given int * parameter.
*
*/
int CSK_INETSocket::getpeername(void *name, int *namelen)
{
if (*namelen < 0)
return SK_Error(EINVAL);
memcpy(name, &fPeerSocketAddress, *namelen = MIN(*namelen, sizeof(struct sockaddr_in)));
return 0;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::shutdown
*
* DESCRIPTION:
*
* Closes all of part of a full-duplex connection on the given socket.
*
* PARAMETERS:
*
* int -- How to close connection: If 0, further receives are
* disabled; if 1, further sends are disabled; if 2
* further sends and receives are disabled.
*
* RETURNS:
*
* The function return value indicates error status (0 = no error). When it is
* -1, a more specific return value is in the global errno.
*
*/
int CSK_INETSocket::shutdown(int how)
{
switch(how) {
case 0 :
fStatus |= SKk_STATUS_NOREAD;
break;
case 1 :
fStatus |= SKk_STATUS_NOWRITE;
break;
case 2 :
fStatus |= SKk_STATUS_NOREAD | SKk_STATUS_NOWRITE;
break;
default :
return SK_Error(EINVAL);
}
return 0;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::fcntl
*
* DESCRIPTION:
*
* Performs a given flag control operation on this socket.
*
* PARAMETERS:
*
* unsigned int -- The command to execute, one of:
*
* F_GETFL returns the descriptor status flags. The only
* flag supported is FNDELAY for non-blocking i/o.
*
* F_SETFL sets descriptor status flags. The only
* flag supported is FNDELAY for non-blocking i/o.
*
* int -- The argument for the command
*
* RETURNS:
*
* Upon successful completion, the value returned depends on
* cmd as follows:
* F_GETFL Value of flags.
* F_SETFL 0.
*
* On error, a value of -1 is returned and errno is set to indicate
* the error.
*
* EBADF s is not a valid open descriptor.
*
* EMFILE cmd is F_DUPFD and socket descriptor table is full.
*
* EINVAL cmd is F_DUPFD and arg is negative or
* greater than the maximum allowable
* number (see SK_getdtablesize).
*
*/
int CSK_INETSocket::fcntl(unsigned int cmd, int arg)
{
switch(cmd) {
/* Get socket status. This is like getsockopt().
Only supported descriptor status is FNDELAY. */
case F_GETFL :
if (fNonBlocking)
return FNDELAY;
else
return 0;
/* Set socket status. This is like setsockopt().
Only supported descriptor status is FNDELAY. */
case F_SETFL :
if (arg & FNDELAY)
fNonBlocking = true;
else
fNonBlocking = false;
return 0;
default:
return SK_Error(EOPNOTSUPP);
}
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocket::ioctl
*
* DESCRIPTION:
*
* Performs a requested io control action on this socket
*
* PARAMETERS:
*
* unsigned int -- Requested control action
* void * -- Pointer to arguments for given action
*
* RETURNS:
*
* The function return value indicates error status (0 = no error). When it is
* -1, a more specific return value is in the global errno.
*
*/
int CSK_INETSocket::ioctl(unsigned int request, void *argp)
{
struct ifreq * ifr;
int size;
/* Interpret high order word to find amount of data to be copied
to/from the user's address space. */
size =(request &~(IOC_INOUT | IOC_VOID)) >> 16;
/* Zero the buffer on the stack so the user gets back something deterministic */
if ((request & IOC_OUT) && size)
bzero((Ptr)argp, size);
ifr =(struct ifreq *)argp;
switch(request) {
/* Non-blocking I/O */
case FIONBIO:
fNonBlocking = (Boolean) *(int *) argp;
return 0;
/* Number of bytes on input Q */
case FIONREAD:
*(unsigned long *) argp = Available();
return 0;
default :
return SK_Error(EOPNOTSUPP);
}
}
/**************************************************************************/
/**************************************************************************/
/* CSK_INETSocketDomain members */
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocketDomain::CSK_INETSocketDomain
*
* DESCRIPTION:
*
* Constructor for this class
*
*/
CSK_INETSocketDomain::CSK_INETSocketDomain()
: CSK_SocketDomain(AF_INET)
{
CSK_Configuration conf; // SKg_Config isn't yet guaranteed to work
fDriverState = 1;
fResolverState = 1;
fDrvrRefNum = 0;
fNumExistingSockets = 0;
/* Allocate storage for pbs */
fLastPBUsed = 0;
fThePBArray = NULL;
if (conf.IsDaemon()) {
if (conf.fTCPDaemon)
AEInstallEventHandler(
'INET', 'TSTR', AEEventHandlerProcPtr(SK_TCPPossession), 0, false);
else
AEInstallEventHandler(
'INET', 'TSTR', AEEventHandlerProcPtr(SK_UnsupportedAEVT), 0, false);
if (conf.fUDPDaemon)
AEInstallEventHandler(
'INET', 'USTR', AEEventHandlerProcPtr(SK_UDPPossession), 0, false);
else
AEInstallEventHandler(
'INET', 'USTR', AEEventHandlerProcPtr(SK_UnsupportedAEVT), 0, false);
}
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocketDomain::Driver
*
* DESCRIPTION:
*
* ???
*
* RETURNS:
*
* short -- The driver reference number (or 0 on error)
*
*/
short CSK_INETSocketDomain::Driver()
{
ParamBlockRec pb;
if (fDriverState == 1) {
pb.ioParam.ioCompletion = 0L;
pb.ioParam.ioNamePtr = "\p.IPP";
pb.ioParam.ioPermssn = fsCurPerm;
fDriverState = PBOpenSync(&pb);
fDrvrRefNum = pb.ioParam.ioRefNum;
}
return fDriverState ? 0 : fDrvrRefNum;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocketDomain::Resolver
*
* DESCRIPTION:
*
* ???
*
* RETURNS:
*
* OSErr -- The resulting resolver state
*
*/
OSErr CSK_INETSocketDomain::Resolver()
{
if (fResolverState == 1)
fResolverState = OpenResolver(nil);
return fResolverState;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_Socket * CSK_INETSocketDomain::socket
*
* DESCRIPTION:
*
* Create a MacTCP socket in this socket domain.
*
* TCP sockets provide sequenced, reliable, two-way connection
* based byte streams.
*
* A TCP socket must be in a connected state before any data may be sent
* or received on it. A connection to another socket is created with a
* connect() call or the listen() and accept() calls. Once connected, data may
* be transferred using read() and write() calls or some variant of the send()
* and recv() calls. When a session has been completed a close() may be
* performed.
*
* A UDP socket supports the exchange of datagrams (connectionless, unreliable
* messages of a fixed maximum length) with correspondents named in send() calls.
* Datagrams are generally received with recv(), which returns the next
* datagram with its return address.
*
* An fcntl() or ioctl() call can be used to enable non-blocking I/O.
*
* PARAMETERS:
*
* int -- The type of socket to create:
*
* SOCK_STREAM to create a TCP socket
* SOCK_DGRAM to create a UDP socket
*
* short -- unused protocol
*
* RETURNS:
*
* The return value is a descriptor referencing the socket or -1
* if an error occurs, in which case global variable errno is
* set to one of:
*
* ENOMEM Failed to allocate memory for the socket
* data structures.
*
* ESOCKTNOSUPPORT Type wasn't SOCK_STREAM or SOCK_DGRAM.
*
* EMFILE The socket descriptor table is full.
*
*/
CSK_Socket * CSK_INETSocketDomain::socket(int type, short)
{
CSK_INETSocket * sock = nil;
errno = 0;
if (!Driver())
return (CSK_Socket *) SK_Error_nil(ENETDOWN);
switch (type) {
case SOCK_STREAM:
sock = new CSK_TCPSocket();
break;
case SOCK_DGRAM:
sock = new CSK_UDPSocket();
break;
default:
SK_Error(ESOCKTNOSUPPORT);
}
if (sock && errno) {
delete sock;
return nil;
}
return sock;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocketDomain::GetPB
*
* DESCRIPTION:
*
* Get a free parameter block from the collection of parameter blocks
* being used by sockets in this domain.
*
* RETURNS:
*
* CSK_AnnotatedPB * -- The free parameter block object
*
* NOTES:
*
* If all parameter blocks are in use, then this routine will loop until
* one becomes available (possibly forever, as I am now finding out).
*
*/
CSK_AnnotatedPB * CSK_INETSocketDomain::GetPB()
{
CSK_AnnotatedPB * curPB;
Boolean once_around = FALSE;
long final_tick;
/* Error check */
ASSERT(fThePBArray != NULL);
/* Try to locate a free parameter block */
do {
if (++fLastPBUsed == SKk_Num_PBs) {
// If already went once around without finding anything, then we give
// up some time to the OS, in the hope that some parameter block will
// be freed upon a completion
if (once_around) SystemTask();
once_around = TRUE;
fLastPBUsed = 0;
}
curPB = fThePBArray + fLastPBUsed;
} while (curPB->Busy());
// Return block
return curPB;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocketDomain::OpenSocket
*
* DESCRIPTION:
*
* ???
*
*/
void CSK_INETSocketDomain::OpenSocket()
{
/* Allocate PB array if this is first socket to be opened */
if (fThePBArray == NULL) {
fThePBArray = new CSK_AnnotatedPB[SKk_Num_PBs];
if (fThePBArray == NULL) {
errno = ENOMEM;
return;
}
}
/* Count this socket */
fNumExistingSockets++;
}
/*************************************************************************** *
* FUNCTION:
*
* CSK_INETSocketDomain::CloseSocket
*
* DESCRIPTION:
*
* ???
*
*/
void CSK_INETSocketDomain::CloseSocket()
{
/* Decrement socket counter */
fNumExistingSockets--;
/* If no sockets left, then deallocate PB array */
if (fNumExistingSockets == 0) {
delete [] fThePBArray;
fThePBArray = NULL;
}
}
/* end of SK_INET.cc */